/*
	Written By Pradipna Nepal
	www.pradsprojects.com

	Copyright (C) 2012 Prads Projects
	Please read COPYING.txt included along with this source code for more detail.
	If not included, see http://www.gnu.org/licenses/
*/

#include "libParser.h"

Parser::Parser() {
	label = 0;
	reset();
}

bool Parser::parseToIR(list<Opcode> *irInstructions, const TokenLines *tokens) {
	instructions = irInstructions;
	tokenLine = tokens;
	return init();
}

bool Parser::loadToReg() {
	if (tokenLine->tokens[tCounter].getToken() == TOKEN_OPENROUNDBRACKET) {
		++tCounter;
		return init();
	} else {
		Opcode temp;
		if (tokenLine->tokens[tCounter].getToken() == TOKEN_IDENTIFIER) {
			temp.ir = LDM_RY;
		} else if (tokenLine->tokens[tCounter].getToken() == TOKEN_KEYWORD) {
			switch (tokenLine->tokens[tCounter].getDescription()) {
				case KW_CTRL1:
					temp.ir = CTRL1;
					break;
				case KW_RANDOM:
					temp.ir = RND;
			}
		} else {
			temp.ir = LDI_RX_HHLL;
		}
		temp.regs = regCounter++;
		temp.operand.push_back(tokenLine->tokens[tCounter++]);
		temp.label = 0;
		instructions->push_back(temp);
	}

	return true;
}

bool Parser::add() {
	++tCounter;
	if (!loadToReg()) return false;
	if (!highPrecedence()) return false;
	
	Opcode temp;
	temp.ir = ADD;
	temp.regs = 0;
	setRegY(temp.regs, --regCounter);
	setRegX(temp.regs, (regCounter - 1));
	temp.label = 0;
	instructions->push_back(temp);

	return true;
}

bool Parser::subtract() {
	++tCounter;
	if (!loadToReg()) return false;
	if (!highPrecedence()) return false;
	
	Opcode temp;
	temp.ir = SUB;
	setRegY(temp.regs, --regCounter);
	setRegX(temp.regs, (regCounter - 1));
	temp.label = 0;
	instructions->push_back(temp);

	return true;
}

bool Parser::highPrecedence() {
	while (true) {
		switch (tokenLine->tokens[tCounter].getDescription()) {
			case OPERATOR_MUL:
				if (!multiply()) return false;
				break;
			case OPERATOR_DIV:
				if (!divide()) return false;
				break;
			case OPERATOR_BIT_AND:
				if (!bitAND()) return false;
				break;
			case OPERATOR_BIT_OR:
				if (!bitOR()) return false;
				break;
			case OPERATOR_BIT_XOR:
				if (!bitXOR()) return false;
				break;
			default:
				return true;
		}
	}
}

bool Parser::multiply() {
	++tCounter;
	if (!loadToReg()) return false;
	
	Opcode temp;
	temp.ir = MUL;
	setRegY(temp.regs, --regCounter);
	setRegX(temp.regs, (regCounter - 1));
	temp.label = 0;
	instructions->push_back(temp);

	return true;
}

bool Parser::divide() {
	++tCounter;
	if (!loadToReg()) return false;

	Opcode temp;
	temp.ir = DIV;
	setRegY(temp.regs, --regCounter);
	setRegX(temp.regs, (regCounter - 1));
	temp.label = 0;
	instructions->push_back(temp);

	return true;
}

bool Parser::bitAND() {
	++tCounter;
	if (!loadToReg()) return false;
	
	Opcode temp;
	temp.ir = AND;
	setRegY(temp.regs, --regCounter);
	setRegX(temp.regs, (regCounter - 1));
	temp.label = 0;
	instructions->push_back(temp);

	return true;
}

bool Parser::bitOR() {
	++tCounter;
	if (!loadToReg()) return false;
	
	Opcode temp;
	temp.ir = OR;
	setRegY(temp.regs, --regCounter);
	setRegX(temp.regs, (regCounter - 1));
	temp.label = 0;
	instructions->push_back(temp);

	return true;
}

bool Parser::bitXOR() {
	++tCounter;
	if (!loadToReg()) return false;
	
	Opcode temp;
	temp.ir = XOR;
	setRegY(temp.regs, --regCounter);
	setRegX(temp.regs, (regCounter - 1));
	temp.label = 0;
	instructions->push_back(temp);

	return true;
}

bool Parser::condition(int c) {
	++tCounter;
	if (!init()) return false;
	
	Opcode temp;

	//CMP RX1, RX2
	temp.ir = CMP;
	int reg0 = --regCounter, reg1 = regCounter - 1;
	temp.regs = (reg0 << 4) | reg1;
	temp.label = 0;
	instructions->push_back(temp);

	//JL lblLessThan
	temp.ir = JX;
	temp.regs = c;
	temp.label = (++label << 16);
	instructions->push_back(temp);

	//XOR RX1, RX1
	temp.ir = XOR;
	temp.regs = ((regCounter - 1) << 4) | (regCounter - 1);
	temp.label = 0;
	instructions->push_back(temp);

	//JMP lblBranch
	temp.ir = JMP_HHLL;
	temp.label = (++label << 16);
	instructions->push_back(temp);

	//lblLessThan: LDI RX1, 1
	temp.ir = LDI_RX_HHLL;
	temp.label = label - 1;		//label has been increment twice above
	temp.operand.push_back(Token("1", TOKEN_DIGIT, DESC_NONE));
	instructions->push_back(temp);

	//lblBranch: NOP
	temp.ir = NOP;
	temp.label = label;
	instructions->push_back(temp);

	//regCounter = 1;

	return true;
}

bool Parser::expression() {
	while (tokenLine->tokens[tCounter].getToken() != TOKEN_SEMICOLON && tokenLine->tokens[tCounter].getToken() != TOKEN_OPENCURLYBRACKET) {
		switch(tokenLine->tokens[tCounter].getDescription()) {
			case OPERATOR_ADD:
				if (!add()) return false;
				break;
			case OPERATOR_SUB:
				if (!subtract()) return false;
				break;
			case OPERATOR_MUL:
				if (!multiply()) return false;
				break;
			case OPERATOR_DIV:
				if (!divide()) return false;
				break;
			case COMPARE_LESS:
			case COMPARE_GREATER:
			case COMPARE_EQUALS:
			case COMPARE_NOT_EQUAL:
			case COMPARE_GREATER_EQUAL:
			case COMPARE_LESS_EQUAL:
				if (!condition(tokenLine->tokens[tCounter].getDescription())) return false;
				break;
			case OPERATOR_BIT_AND:
			case OPERATOR_LOG_AND:
				if (!bitAND()) return false;
				break;
			case OPERATOR_BIT_OR:
			case OPERATOR_LOG_OR:
				if (!bitOR()) return false;
				break;
			case OPERATOR_BIT_XOR:
				if (!bitXOR()) return false;
				break;
			
			//	if (!logicAnd()) return false;
			//	break;
			//	if (!logicOr()) return false;
			//	break;
			default:
				if (tokenLine->tokens[tCounter].getToken() == TOKEN_CLOSEROUNDBRACKET) {
					++tCounter;
					return true;
				} else {
					string err;

					err = "Unexpected symbol: " + tokenLine->tokens[tCounter].getValue();
					printError(ERROR_SYNTAX, err.c_str());
					return false;
				}
		}
	}

	return true;
}

bool Parser::init() {
	if (!loadToReg()) return false;
	return expression();
}

void Parser::reset() {
	tCounter = 0;
	regCounter = 0;
}

void Parser::setTokenCounter(int tCounter) {
	this->tCounter = tCounter;
}

int Parser::getLabelOut(bool pop) {
	int retVal = lblStackOut.top();
	if (pop) lblStackOut.pop();
	return retVal;
}

int Parser::getLabelIn(bool pop) {
	int retVal = lblStackIn.top();
	if (pop) lblStackIn.pop();
	return retVal;
}

void Parser::pushLabels(bool in, bool out) {
	if (in) lblStackIn.push(++label);
	if (out) lblStackOut.push(++label);
}

int Parser::getCurrentLabel(bool increment) {
	return (increment) ? ++label : label;
}